//////////////////////////////////////////////////////////////////////////////////
//	 Copyright 2011 by Yosef Grabivker, ryuho@homenetworksoft.com
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//////////////////////////////////////////////////////////////////////////////////

#include <wx/wx.h>
#include "ar7modem.h"
#include "../common/MarkupSTL.h"
#include "logicmanager.h"



CAR7Modem::MODESELECTOR CAR7Modem::gModes[]= {
{	0x0,	"NMODE",				_("Idle"),					_("Not connected"),			0,			0		},
{	0x1,	"MMODE",				_("Multi Mode"),			_("Auto"),					0,			0		},
{	0x2,	"T1413",				_("ANSI T1.413"),			wxT("ADSL"),					15*1024,	1.5*1024},
{	0x3,	"GDMT",					_("G.DMT"),					_("ADSL"),					12*1024,	1.3*1024},
{	0x4,	"GLITE",				_("G.Lite"),				_("ADSL, Provides greater resistance to noise"), 1.5*1024,	128},
{	0x8,	"AD2MOD",				_("ADSL G.dmt.bis"),		_("ADSL2"),					12*1024,	3.5*1024},
{	0x9,	"AD2DEL",				_("ADSL G.dmt.bis_DELT"),	_("ADSL2"),					12*1024,	3.5*1024},
{	0x10,	"A2PMOD",				_("ADSL2+"),				_("ADSL2"),					24*1024,	1.4*1024},
{	0x11,	"A2PDEL",				_("ADSL2+ DELT"),			_("ADSL2"),					24*1024,	1.4*1024},
{	0x400,	"ADSL_G.dmt.bis_AnxM",	_("ADSL2, G.dmt.bis Annex M"),_("ADSL2+ Annex M, Double upstream"),			24*1024,	3*1024},
{	0x2000, "ADSL_2plus_AnxM",		_("ADSL2+, Annex M"),		_("ADSL2+ Annex M, Double upstream"),	24*1024,	3*1024},
};


CAR7Modem::CAR7Modem(CLinkGuard& link):CADSLModem(link)
{
}

CAR7Modem::~CAR7Modem(void)
{
}

unsigned char transform_asis(const char* szDigit)
{
	return (unsigned char)atol(szDigit);
}

unsigned char transform_snr(const char* szDigit)
{
	long l;
	sscanf( szDigit, "%x", &l);

	short d = (short)l;

	//double t = l; /*ntohs( (unsigned short)l );*/

	d/=85.04; 

	return (unsigned char) (d>0?d:0);
}


std::string CAR7Modem::GetModemStats(std::string& strRawData)
{
	std::string strRet;

	RTSTRING strData = m_link->ReadFile(CMD_GET_ADSL_STATS);

	if ( !strData.empty() )
	{
		CMarkupSTL	doc;
		doc.AddElem( "adsl" );

		strRawData=strData;

		NAMEDVALS lst;

		if ( CLogicManager::ExtractPairs(":", strData, lst) )
		{
			doc.AddChildElem( ADSL_STAT_TAG  );

			for( size_t i=0;i<lst.size();i++ )
			{
				std::string strName = lst[i].strLabel;
				std::string strVal  = lst[i].strVal;

				if (strName=="LOS errors")
				{
					doc.AddChildAttrib(	ADSL_LOS_ERR_ATTR, strVal.c_str() );
				}

				if (strName=="Total init. errors")
				{
					doc.AddChildAttrib(	ADSL_INIT_ERR_ATTR, strVal.c_str() );
				}

				if (strName=="SEF errors")
				{
					doc.AddChildAttrib(	ADSL_SEF_ERR_ATTR, strVal.c_str() );
				}

				if (strName == "DS Margin" )
				{
					doc.AddChildAttrib(	ADSL_D_SNR_ATTR, strVal.c_str() );
				}

				if (strName == "US Margin" )
				{
					doc.AddChildAttrib(	ADSL_U_SNR_ATTR, strVal.c_str() );
				}

				if ( 0==strName.find("DS Line Attenuation") )
				{
					doc.AddChildAttrib( ADSL_D_ATTN_ATTR, strVal.c_str() );
				}

				if ( 0==strName.find("US Line Attenuation") )
				{
					doc.AddChildAttrib( ADSL_U_ATTN_ATTR, strVal.c_str() );
				}

				if ( 0==strName.find("US Connection Rate") )
				{
					doc.AddChildAttrib( ADSL_U_CONRATE_ATTR,  strVal.c_str() );
				}

				if ( 0==strName.find("DS Connection Rate") )
				{
					doc.AddChildAttrib( ADSL_D_CONRATE_ATTR,  strVal.c_str() );
				}

				if ( 0==strName.find("Trained Mode") )
				{
					doc.AddChildAttrib( ADSL_ISUP_ATTR,  strVal=="0"?"false":"true" );
					doc.AddChildAttrib(	ADSL_MODE_ATTR,  ConvertToModeString( atol(strVal.c_str()) ).c_str() );
				}

				if ( 0==strName.find("Selected Mode") )
				{
					doc.AddChildAttrib(	ADSL_MODE_SEL_ATTR,  atol( strVal.c_str() ) );
				}

				if ( 0==strName.find("Showtime Count") )
				{
					doc.AddChildAttrib(	ADSL_DISC_COUNT_ATTR,  atol(strVal.c_str())  );
				}

				if ( 0==strName.find("Trellis") )
				{
					doc.AddChildAttrib(	ADSL_TRELLIS_ATTR,  atol(strVal.c_str())  );
				}

				if ( 0==strName.find("BitSwap") )
				{
					doc.AddChildAttrib(	ADSL_BITSWAP_ATTR,  atol(strVal.c_str())  );
				}
			}
		}

		size_t stUPCRC(0), stUPFEC(0), stUPNCD(0),  stUPLCD(0), stUPHEC(0);
		size_t stDNCRC(0), stDNFEC(0), stDNNCD(0),  stDNLCD(0), stDNHEC(0);

		NAMEDVALS lstErrUp, lstErrDn;

		CLogicManager::GetSection(strData, "Upstream (TX) Interleave path", lstErrUp);
		CLogicManager::GetSection(strData, "Upstream (TX) Fast path", lstErrUp);

		for( size_t i=0;i<lstErrUp.size();i++ )
		{
			std::string strName = lstErrUp[i].strLabel;
			size_t stVal  = atol( lstErrUp[i].strVal.c_str());

			if ( 0==strName.find("CRC") )
			{
				stUPCRC+=stVal;
			}

			if ( 0==strName.find("FEC") )
			{
				stUPFEC+=stVal;
			}

			if ( 0==strName.find("NCD") )
			{
				stUPNCD+=stVal;
			}

			if ( 0==strName.find("LCD") )
			{
				stUPLCD+=stVal;
			}

			if ( 0==strName.find("HEC") )
			{
				stUPHEC+=stVal;
			}
		}

		CLogicManager::GetSection(strData, "Downstream (RX) Interleave path", lstErrDn);
		CLogicManager::GetSection(strData, "Downstream (RX) Fast path", lstErrDn);

		for( size_t i=0;i<lstErrDn.size();i++ )
		{
			std::string strName = lstErrDn[i].strLabel;
			size_t stVal  = atol( lstErrDn[i].strVal.c_str());

			if ( 0==strName.find("CRC") )
			{
				stDNCRC+=stVal;
			}

			if ( 0==strName.find("FEC") )
			{
				stDNFEC+=stVal;
			}

			if ( 0==strName.find("NCD") )
			{
				stDNNCD+=stVal;
			}

			if ( 0==strName.find("LCD") )
			{
				stDNLCD+=stVal;
			}

			if ( 0==strName.find("HEC") )
			{
				stDNHEC+=stVal;
			}
		}

		doc.AddChildAttrib(	ADSL_U_CRC_ERR_ATTR,  stUPCRC  );
		doc.AddChildAttrib(	ADSL_U_FEC_ERR_ATTR,  stUPFEC  );
		doc.AddChildAttrib(	ADSL_U_NCD_ERR_ATTR,  stUPNCD  );
		doc.AddChildAttrib(	ADSL_U_LCD_ERR_ATTR,  stUPLCD  );
		doc.AddChildAttrib(	ADSL_U_HEC_ERR_ATTR,  stUPHEC  );

		doc.AddChildAttrib(	ADSL_D_CRC_ERR_ATTR,  stDNCRC  );
		doc.AddChildAttrib(	ADSL_D_FEC_ERR_ATTR,  stDNFEC  );
		doc.AddChildAttrib(	ADSL_D_NCD_ERR_ATTR,  stDNNCD  );
		doc.AddChildAttrib(	ADSL_D_LCD_ERR_ATTR,  stDNLCD  );
		doc.AddChildAttrib(	ADSL_D_HEC_ERR_ATTR,  stDNHEC  );

		RTSTRING strData = m_link->SendCmd(wxT("cat /proc/avalanche/avsar_bit_allocation_table"), 10000 );
		if ( !strData.empty() )
		{

			CLogicManager::INTLIST u = CLogicManager::ParseDataTable(strData, "DSL Modem US", transform_asis);
			CLogicManager::INTLIST d = CLogicManager::ParseDataTable(strData, "DSL Modem DS", transform_asis);

			if ( !u.empty() )
			{
				doc.AddChildElem( ADSL_BITS_U_TAG  );
				doc.IntoElem();

				for( size_t i=0;i<u.size();i++ )
				{
					doc.AddChildElem( ADSL_TONE_ATTR  );
					doc.AddChildAttrib( ADSL_TONE_ID_ATTR, i  );
					doc.AddChildAttrib( ADSL_TONE_VAL_ATTR, u[i]  );
				}

				doc.OutOfElem();
			}

			if ( !d.empty() )
			{
				doc.AddChildElem( ADSL_BITS_D_TAG  );
				doc.IntoElem();

				for( size_t i=0;i<d.size();i++ )
				{
					doc.AddChildElem( ADSL_TONE_ATTR  );
					doc.AddChildAttrib( ADSL_TONE_ID_ATTR, i  );
					doc.AddChildAttrib( ADSL_TONE_VAL_ATTR, d[i]  );
				}

				doc.OutOfElem();
			}
		}

		strData = m_link->SendCmd(wxT("cat /proc/avalanche/avsar_rxsnr0"), 10000 );
		if ( !strData.empty() )
		{
			CLogicManager::INTLIST l = CLogicManager::ParseDataTable(strData, "DSL Modem", transform_snr);

			if ( !l.empty() )
			{
				doc.AddChildElem( ADSL_SNR1_BIT_TAG  );
				doc.IntoElem();

				for( size_t i=0;i<l.size();i++ )
				{
					doc.AddChildElem( ADSL_TONE_ATTR  );
					doc.AddChildAttrib( ADSL_TONE_ID_ATTR, i  );
					doc.AddChildAttrib( ADSL_TONE_VAL_ATTR, l[i]  );
				}

				doc.OutOfElem();
			}
		}

		strRet = doc.GetDoc();
	}

	return strRet;
}

std::string CAR7Modem::GetCaps()
{
	CMarkupSTL	doc;
	doc.AddElem( ADSL_CAPS );

	doc.AddAttrib( GLOBAL_NAME_ATR,		"AR7" );

	//Modes
	doc.AddChildElem( XMLUI_BLOCK_TAG );
	doc.AddChildAttrib( XMLUI_BLOCK_NAME_ATR, (const char*)wxString( _("Modulation:") ).utf8_str() );
	doc.AddChildAttrib( XMLUI_ELEMENT_BLOCK_ID_ATTR,	"mode" );

	char buf[256];
	
	doc.IntoElem();
	for(size_t i=0;i<sizeof(gModes)/sizeof( gModes[0]);++i)
	{
		MODESELECTOR& mode = gModes[i];

		sprintf(buf, "tmode%x", mode.wModeID);
		
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_RADIO );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,	buf );
		doc.AddChildAttrib( XMLUI_ELEMENT_LOOKUP_ID_ATTR,	mode.wModeID );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	mode.strUserName.utf8_str() );
		doc.AddChildAttrib( XMLUI_MODE_DESC_ATTR,	mode.strDescription.utf8_str() );
		doc.AddChildAttrib( XMLUI_VAL_ATTR,			ADSL_MODE_SEL_ATTR );
		doc.AddChildAttrib( XMLUI_VAL_TO_ATTR,		"echo %s> /proc/sys/dev/dslmod");
	}

	//Apply
	doc.AddChildElem( XMLUI_UI_TAG );
	doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,		"apply_mode" );
	doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_BUTTON );
	doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString(_("Apply") ).utf8_str() 	);
	doc.AddChildAttrib( XMLUI_UI_ATTR_BTN_APP_BLOCK, "mode");

	doc.OutOfElem();

	//Actions
	doc.AddChildElem( XMLUI_BLOCK_TAG );
	doc.AddChildAttrib( XMLUI_BLOCK_NAME_ATR, wxString( _("Actions:") ).utf8_str() );
	doc.IntoElem();
	{
		/*
		//Idle
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,		"Idle" );
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_BUTTON );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	 wxString(_("Disconnect") ).utf8_str() 	);
		doc.AddChildAttrib( XMLUI_VAL_TO_ATTR,		"echo \"idle\" > /proc/sys/dev/dslmod");
		*/

		//Retrain
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,		"retrain" );
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_BUTTON );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	 wxString( _("Retrain")	).utf8_str() 	);
		doc.AddChildAttrib( XMLUI_VAL_TO_ATTR,		"echo \"retrain\" > /proc/sys/dev/dslmod");

		//Apply
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,	"apply_all" );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_BUTTON );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString(_("Apply All") ).utf8_str() );
		doc.AddChildAttrib( XMLUI_UI_ATTR_BTN_APP_BLOCK, "all");
	}
	doc.OutOfElem();

	//Settings
	doc.AddChildElem( XMLUI_BLOCK_TAG );
	doc.AddChildAttrib( XMLUI_BLOCK_NAME_ATR, wxString( _("Settings:") ).utf8_str() );
	doc.AddChildAttrib( XMLUI_ELEMENT_BLOCK_ID_ATTR,	"settings" );
	doc.IntoElem();
	{
		//losalarmon
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,		"losalarm" );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_CHECK );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString( _("LOS Alarm") ).utf8_str()	);
		doc.AddChildAttrib( XMLUI_VAL_TO_ON_ATTR,	"echo \"losalarmon\" > /proc/sys/dev/dslmod");
		doc.AddChildAttrib( XMLUI_VAL_TO_OFF_ATTR,	"echo \"losalarmoff\"> /proc/sys/dev/dslmod");

		//trellison
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,		"trellis" );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_CHECK );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString( _("Trellis") ).utf8_str()		);
		doc.AddChildAttrib( XMLUI_VAL_TO_ON_ATTR,	"echo \"trellison\" > /proc/sys/dev/dslmod");
		doc.AddChildAttrib( XMLUI_VAL_TO_OFF_ATTR,	"echo \"trellisoff\"> /proc/sys/dev/dslmod");
		doc.AddChildAttrib( XMLUI_VAL_ATTR,			ADSL_TRELLIS_ATTR );

		//bitswap
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,		"bitswap" );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_CHECK );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString( _("Bitswap") ).utf8_str() 	);
		doc.AddChildAttrib( XMLUI_VAL_TO_ON_ATTR,	"echo \"bitswapon\" > /proc/sys/dev/dslmod");
		doc.AddChildAttrib( XMLUI_VAL_TO_OFF_ATTR,	"echo \"bitswapoff\"> /proc/sys/dev/dslmod");
		doc.AddChildAttrib( XMLUI_VAL_ATTR,			ADSL_BITSWAP_ATTR );

		//gainctl
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,		"gainctl" );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_CHECK );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString( _("Fine Gain") ).utf8_str()	);
		doc.AddChildAttrib( XMLUI_VAL_TO_ON_ATTR,	"echo \"gainctlon\" > /proc/sys/dev/dslmod");
		doc.AddChildAttrib( XMLUI_VAL_TO_OFF_ATTR,	"echo \"gainctloff\"> /proc/sys/dev/dslmod");


		//gainv
		//os_atoh
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,		"gainv" );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_INPUT );
		doc.AddChildAttrib( XMLUI_UI_ATTR_INPUT_TYPE, XMLUI_UI_ATTR_INPUT_TYPE_D );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString( _("Fine Gain value") ).utf8_str() );
		doc.AddChildAttrib( XMLUI_VAL_TO_ATTR,	"echo gainv%x > /proc/sys/dev/dslmod");

		//idepth
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,		"idepth" );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_INPUT );
		doc.AddChildAttrib( XMLUI_UI_ATTR_INPUT_TYPE, XMLUI_UI_ATTR_INPUT_TYPE_D );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString( _("Interleaver Depth") ).utf8_str() );
		doc.AddChildAttrib( XMLUI_VAL_TO_ATTR,	"echo idepth%d > /proc/sys/dev/dslmod");

		//carrierperbit
		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,		"carrierperbit" );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_INPUT );
		doc.AddChildAttrib( XMLUI_UI_ATTR_INPUT_TYPE, XMLUI_UI_ATTR_INPUT_TYPE_D );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString( _("Bits Per Carrier Upstream") ).utf8_str() );
		doc.AddChildAttrib( XMLUI_VAL_TO_ATTR,	"echo carrierperbit%x > /proc/sys/dev/dslmod");

		doc.AddChildElem( XMLUI_UI_TAG );
		doc.AddChildAttrib( XMLUI_ELEMENT_ID_ATTR,	"apply_settings" );
		doc.AddChildAttrib( XMLUI_TYPE_ATR,			XMLUI_UI_ATTR_BUTTON );
		doc.AddChildAttrib( XMLUI_MODE_NAME_ATTR,	wxString(_("Apply") ).utf8_str() 	);
		doc.AddChildAttrib( XMLUI_UI_ATTR_BTN_APP_BLOCK, "settings");
	}
	doc.OutOfElem();

	return doc.GetDoc();
}

std::string	CAR7Modem::ConvertToModeString(long lMode )
{
	std::string strRes = "Unknown";

	for(size_t i=0;i<sizeof(gModes)/sizeof( gModes[0]);++i)
	{
		MODESELECTOR& mode = gModes[i];

		if ( mode.wModeID == lMode )
		{
			return (const char*)mode.strUserName.ToAscii();
		}
	}

	return strRes;
}

void CAR7Modem::SetUpModem(const std::string& strModemSettings)
{
	CMarkupSTL	doc;

	if ( doc.SetDoc(strModemSettings.c_str()) )
	{
		doc.FindElem();

		if ( doc.GetAttrib(XMLGUI_EXEC_ATTR_STARTUP)=="true" )
		{
			ExecuteXML(strModemSettings);
		}
	}
}

/*
<exec startup="false">
<exec_item id="4">echo 4&gt; /proc/sys/dev/dslmod</exec_item>
<exec_item id="losalarm">echo "losalarmoff"&gt; /proc/sys/dev/dslmod</exec_item>
<exec_item id="trellis">echo "trellison" &gt; /proc/sys/dev/dslmod</exec_item>
<exec_item id="bitswap">echo "bitswapoff"&gt; /proc/sys/dev/dslmod</exec_item>
<exec_item id="gainctl">echo "gainctloff"&gt; /proc/sys/dev/dslmod</exec_item>
</exec>
*/
bool CAR7Modem::ExecuteXML(const std::string& strModemSettings)
{
	bool bRes(false);

	CMarkupSTL	doc;

	if ( doc.SetDoc(strModemSettings.c_str()) )
	{
		while( doc.FindChildElem(XMLGUI_EXEC_ITEM_EL) )
		{
			std::string strExec = doc.GetChildData();

			if ( !strExec.empty() )
			{
				wxString strCMD = wxString::FromAscii( strExec.c_str() );
				std::string strData = m_link->SendCmd(strCMD, 200 );

				if ( std::string::npos==strData.find("cannot create") &&
					std::string::npos==strData.find("such file") )
				{
					bRes|= true;
				}
			}
		}
	}

	return bRes;
}


